package it.uniroma2.tesi.bellomaria;

import java.io.File;
import java.io.FileNotFoundException;
import java.net.MalformedURLException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RssParser {

	public static String pathLavoro = new String();
	public static String dataElaborazione = new SimpleDateFormat("yyyy-MM-dd").format(new Date());

	Vector<String> films = new Vector<String>();
	HashMap<String, Film> map_film = new HashMap<String, Film>();
	HashMap<String, Film> map_film_added = new HashMap<String, Film>();
	HashMap<String, Persona> map_persona = new HashMap<String, Persona>();
	HashMap<String, Persona> map_persona_added = new HashMap<String, Persona>();
	Vector<FilmHasPersona> filmHasPersona = new Vector<FilmHasPersona>();
	Vector<FilmHasPersona> filmHasPersona_added = new Vector<FilmHasPersona>();
	HashMap<String, Integer> elenco_cinema = new HashMap<String, Integer>();
	Vector<String> proiezioni = new Vector<String>();
	HashMap<String, Sala> map_sale = new HashMap<String, Sala>();
	Vector<SalaProiettaFilm> salaProiettaFilm = new Vector<SalaProiettaFilm>();
	
	public static void main(String[] args) {

		RssParser parser = new RssParser();
		RunScript rs = new RunScript();

		// il metodo main necessita di 2 argomenti: operazioni da eseguire, directory di lavoro.
		if(args.length==0 || args[0].equals("-help")){
			System.out.println("PRIMO PARAMETRO OBBLIGATORIO, VALORI AMMISSIBILI : ");
			System.out.println("[-all] scarica da internet tutti i file e carica il db;");
			System.out.println("[-download] scarica da internet tutti i file;");
			System.out.println("[-load] carica il db dai file presenti in [secondo parametro]/script;");
			System.out.println("[-update] aggiorna il database con i nuovi film e programmazioni;");
			System.out.println("SECONDO PARAMETRO OBBLIGATORIO : ");
			System.out.println("[path lavoro]");
			System.out.println("TERZO PARAMETRO FACOLTATIVO : ");
			System.out.println("[data_elaborazione(yyyy-mm-gg)] specifica la data da dove recuperare gli script sql per alimentare il database. Da utilizzare insieme al parametro -update_load");
			
			System.exit(0);
		}
		
		if(args.length<2){
			System.out.println("Parametri obbligatori : -help per aiuto");
			System.exit(0);
		}		

		String parametro = args[0];
		parametro=parametro.toLowerCase();		
		pathLavoro = args[1];
		
		if(parametro.equals("-all")){
			parser.generateScript("create"); // genera gli script
			rs.executeScript("create"); // esegue gli script.
		}else if(parametro.equals("-download")){
			parser.generateScript("create");			
		}else if(parametro.equals("-load")){
			rs.executeScript("create");
		}else if(parametro.equals("-update")){
			parser.generateScript("update");
			rs.executeScript("update");
		}else if(parametro.equals("-update_load")){
			if(args.length==3)
				dataElaborazione = args[2];
			rs.executeScript("update");
		}else {
			System.out.println("primo parametro in input non valido");
			System.exit(0);
		}
	}

	public void generateScript(String mode) {

		// carica l'elenco dei cinema dallo script.
		loadCinema();
		System.out.println("Cinema caricati correttamente.");
		
		// si caricano i film, si estraggono i campi e si generano gli script per caricare le tabelle
		// film, persona e Film_has_persona dal sito MyMovies
		loadFilm();
		System.out.println("Film caricati correttamente.");
		getFilmFields(mode); 
		makeScriptFilm(mode); 
		makeScriptPersona(mode);
		makeScriptFilm_Has_Persona(mode);

		// si carica la programmazione dei film dal sito Nottolasera.it
		loadProiezioni();
		System.out.println("Proiezioni caricate correttamente.");
		// si recuperano le informazioni.
		getProiezioneFields();
		makeScriptSala(mode);
		makeScriptSala_Proietta_Film(mode);
		System.out.println("Script Generati");
	}

	// metodo che carica l'elenco dei cinema in una hasmap con key=nome cinema, value=id_cinema
	@SuppressWarnings("deprecation")
	public void loadCinema() {
		Vector<String> cinema = new Vector<String>();
		File f = new File(pathLavoro+"/script/scriptCinema.sql");
		
		String url = null;		
		try{
			url = f.toURL().toString(); 
		}catch(MalformedURLException e){
			e.printStackTrace();
		}
		
		String script_cinema = Utility.readPage(url, "UTF-8");
		String elemento = "";

		// scrive ogni insert dello script in una posizione del vettore cinema.
		while (script_cinema != null) {
			elemento = Utility.getSubstring(script_cinema, "VALUES (", ");", true);
			if (elemento == null) {
				Utility.resetInedex(); 
				break;
			}
			cinema.add(elemento);
		}

		// recupera id e nome di ogni cinema e li inserisce nell'hashmap.
		for (int i = 0; i < cinema.size(); i++) {
			String s = cinema.elementAt(i);
			String value_id = Utility.getSubstring(s, "\'", "\',", true);
			String key_nome = Utility.getSubstring(s, "\'", "\',", true);
			elenco_cinema.put(key_nome, new Integer(value_id));
			Utility.resetInedex(); 
		}
	}

	// metodo che carica il testo compreso fra i tag <item> </item> contenente i dati del film , 
	// come stringa in un vettore.
	public void loadFilm() {
		String page = null;
		String cleanedPage = null;

		// si legge la pagina all'url passato come parametro, con la codifica specificata. 
		page = Utility.readPage("http://www.mymovies.it/cinema/xml/rss/?id=roma", "iso-8859-1");
		// si ripulisce la pagina dai caratteri speciali.
		cleanedPage = Utility.cleanXML(page); 
		// si scrive con utf-8 la pagina ripulita in un file. 
		Utility.writeUtf8(cleanedPage,pathLavoro+"/rssUtf8.rss", true);

		String rss = "";
		// si trasforma il file scritto in stringa e si memorizza in rss.
		try {
			rss = Utility.fileToString(pathLavoro+"/rssUtf8.rss");
		} catch (FileNotFoundException e) {
			System.out.println("File non trovato.");
			e.printStackTrace();
		}
		String film = "";
		while (rss != null) {
			film = Utility.getSubstring(rss, "<item>", "</item>", true);
			if (film == null) {
				Utility.resetInedex(); // resetta l'indice
				break;
			}
			films.add(film);
		}
	}

	// metodo che scorre il vettore dei film e spezzetta ogni stringa in sottostringhe
	// corrispondenti agli attributi del film.
	public void getFilmFields(String mode) {

		Film f = null;
		Persona p = null;
		Persona appPersona = null;
		FilmHasPersona fhp = null;
		int idpersona = 0;
		int idpersona_new = 0;
		int idFilm = 0;
		boolean new_film = false;
		
		if(mode.equals("update")){
			idFilm = Film.getMaxIdFilm();
			idpersona_new = Persona.getMaxIdPersona();
		}
		
		for (int i = 0; i < films.size(); i++) {
			String elemento = films.elementAt(i);
			f = new Film();
			f.idfilm = i + 1;
			f.titolo = Utility.getSubstring(elemento, "<title>", "</title>", false);
			f.titolo = Utility.cleanFilmTitle(f.titolo).trim();
			
			// link alla pagina del film, contenente la trama
			f.link = Utility.getSubstring(elemento, "<link>", "</link>", false); 

			f.locandina = Utility.getSubstring(elemento, "<img src= \"", "\" align", false);
			f.ind_gradimento = Utility.getSubstring(elemento, "critica e pubblico: ", " stelle", false);
			if (f.ind_gradimento != null)
				f.ind_gradimento = f.ind_gradimento.replace(',', '.');
			String app = Utility.getSubstring(elemento, "Genere", "/a>", false);
			f.genere = Utility.getSubstring(app, ">", "<", false);
			f.anno = Utility.getSubstring(elemento, "?anno=", 4);
			f.durata = Utility.getSubstring(elemento, "Durata ", " minuti", false);
			f.desc_breve = Utility.getSubstring(elemento, "padding:0px;\" >", "</b>", false).trim();

			// si accede all'url del film
			String pageFilm = Utility.readPage(f.getLink(), "iso-8859-1");
			// si estrae il primo paragrafo <p></p>
			pageFilm = Utility.getSubstring(pageFilm, "<p>", "</p>", false);

			// si ripulisce la trama dai caratteri speciali.
			String cleanedPage = Utility.cleanXML(pageFilm);
			cleanedPage = Utility.cleanTrama(cleanedPage);
			f.setTrama(cleanedPage.trim());

			new_film = false;
			if(mode.equals("update")){
				int id = Film.findFilm(f.titolo);
				if(id == -1){
					f.idfilm = ++idFilm;
					map_film_added.put(f.getTitolo().toLowerCase(), f);
					new_film = true;
				}else{
					f.idfilm = id;
				}
			}
			
			// si inserisce l'oggetto film nel campo value dell'hashmap
			// corrispondente, key = titolo.
			map_film.put(f.getTitolo().toLowerCase(), f);

			// recupero dei campi di regista e attori
			String registi = Utility.getSubstring(elemento, "Regia di", "/a>.", false);
			String regista = null;

			while (registi != null) { // entra nel while solo se ci sono registi
				regista = Utility.getSubstring(registi, "\">", "<", true);
				if (regista == null)
					break;

				if(regista.length()>45){
					System.out.println("Regista maggiore 45 caratteri : "+regista);
					regista = regista.substring(0, 45);
				}

				p = new Persona();
				p.nome_cognome = regista;
				p.ruolo = "regista";

				// riempimento dell' hasmap persona senza duplicati
				appPersona = map_persona.put(regista + p.ruolo, p);
				if (appPersona == null)
					p.idPersona = ++idpersona;
				else
					p.idPersona = appPersona.idPersona;

				// riempimento hasmap film_has_persona con i registi
				
				if(mode.equals("update")){
					int id = Persona.findPersona(p.nome_cognome, p.ruolo);
					if(id == -1){
						p.idPersona = ++idpersona_new;
						appPersona = map_persona_added.put(regista + p.ruolo, p);
						if (appPersona != null)
							p.idPersona = appPersona.idPersona;
					}else{
						p.idPersona = id;
					}
				}

				fhp = new FilmHasPersona();
				fhp.idfilm = f.idfilm;
				fhp.idpersona = p.idPersona;
				filmHasPersona.add(fhp);
				if (new_film)
					filmHasPersona_added.add(fhp);
				
				System.out.println("Id Film : "+fhp.getIdfilm()+ ", Titolo : " + f.titolo+ ", con " + p.ruolo+" : "+ p.idPersona+ "-" +p.nome_cognome);
			}
			Utility.resetInedex(); 

			String attori = Utility.getSubstring(elemento, "Con <a", "<br />", false);
			String attore = null;
			while (attori != null) { 
				// si entra nel while solo se ci sono attori
				attore = Utility.getSubstring(attori, ">", "</a>", true);
				if (attore == null)
					break;
				
				if(attore.length()>45){
					System.out.println("Attore maggiore 45 caratteri : "+attore);
					attore = attore.substring(0, 45);
				}

				p = new Persona();
				p.nome_cognome = attore;
				p.ruolo = "attore";

				// riempimento hasmap persona senza duplicati
				appPersona = map_persona.put(attore + p.ruolo, p);
				if (appPersona == null)
					p.idPersona = ++idpersona;
				else
					p.idPersona = appPersona.idPersona;

				if(mode.equals("update")){
					int id = Persona.findPersona(p.nome_cognome, p.ruolo);
					if(id == -1){
						p.idPersona = ++idpersona_new;
						appPersona = map_persona_added.put(attore + p.ruolo, p);
						if (appPersona != null)
							p.idPersona = appPersona.idPersona;
					}else{
						p.idPersona = id;
					}
				}

				// riempimento hasmap film_has_persona con gli attori
				fhp = new FilmHasPersona();
				fhp.idfilm = f.idfilm;
				fhp.idpersona = p.idPersona;
				filmHasPersona.add(fhp);
				if (new_film)
					filmHasPersona_added.add(fhp);
				
				System.out.println("Id Film : "+fhp.getIdfilm()+ ", Titolo : " + f.titolo+ ", con " + p.ruolo+" : "+ p.idPersona+ "-" +p.nome_cognome);
			}
			Utility.resetInedex(); 

		}// end for
	}

	// metodo che crea lo script sql che popola la tabella film del db.
	public void makeScriptFilm(String mode) {
		
		StringBuffer query = new StringBuffer();
		Iterator<Film> iter = null;
		// lettura dei campi dalla hasmap e creazione dello script di insert
		// nella tabella film
		if(mode.equals("create"))
			iter = map_film.values().iterator();
		else
			iter = map_film_added.values().iterator();

		String insertFilm = null; 

		// costruzione della query per ogni elemento della hasmap.
		while (iter.hasNext()) {
			Film fhm = (Film) iter.next();

			// costruzione della query sql di INSERT INTO Film.
			insertFilm = "INSERT INTO dbcinema.film VALUES('" + fhm.getIdfilm() + "','" + fhm.getTitolo() + "','" + fhm.getGenere() + "','" + fhm.getAnno() + "','"
					+ fhm.getDurata() + "','" + fhm.getInd_gradimento() + "','" + fhm.getTrama() + "','" + fhm.getDesc_breve() + "','" + fhm.getLocandina() + "');\r\n";

			Pattern re = Pattern.compile("''");
			Matcher m = re.matcher(insertFilm);
			insertFilm = m.replaceAll("null");

			re = Pattern.compile("'null'");
			m = re.matcher(insertFilm);
			insertFilm = m.replaceAll("null");

			// agginge la insert al vettore delle query per la tabella film.
			query.append(insertFilm);
		}
		// scrive il vettore delle query nel file "scriptFilm.sql"
		Utility.writeUtf8(query.toString(), Utility.getScriptPath(mode)+"/scriptFilm.sql", false);
	}

	// metodo che crea lo script sql che popola la tabella persona del db.
	public void makeScriptPersona(String mode) {
		
		StringBuffer query = new StringBuffer();
		Iterator<Persona> iter = null; 

		// lettura dei campi dalla hasmap e creazione dello script di insert
		if(mode.equals("create"))
			iter = map_persona.values().iterator();
		else
			iter = map_persona_added.values().iterator();

		String insertPersona = null; 

		// costruzione della query per ogni elemento della hasmap.
		while (iter.hasNext()) {
			Persona p = (Persona) iter.next();

			// costruzione della query sql di INSERT INTO Persona.
			insertPersona = "INSERT INTO dbcinema.persona VALUES('" + p.getIdPersona() + "','" + p.getNome_cognome()+ "','" + p.getRuolo() + "');\r\n";

			Pattern re = Pattern.compile("''");
			Matcher m = re.matcher(insertPersona);
			insertPersona = m.replaceAll("null");

			// agginge la insert al vettore delle query per la tabella Persona.
			query.append(insertPersona);
			// scrittura della insert nel file script.
		}
		Utility.writeUtf8(query.toString(), Utility.getScriptPath(mode)+"/scriptPersona.sql", false);
	}

	// metodo che crea lo script sql che popola la tabella film_has_persona del db.
	public void makeScriptFilm_Has_Persona(String mode) {
		
		StringBuffer query = new StringBuffer();
		Iterator<FilmHasPersona> iter = null ;

		// lettura dei campi dalla hasmap e creazione dello script di insert.
		if(mode.equals("create"))
			iter = filmHasPersona.iterator();
		else
			iter = filmHasPersona_added.iterator();

		String insertFHP = null; 

		// costruzione della query per ogni elemento del vettore.
		while (iter.hasNext()) {
			FilmHasPersona fhp = (FilmHasPersona) iter.next();

			// costruzione della query sql di INSERT INTO Film Has Persona.
			insertFHP = "INSERT INTO dbcinema.film_has_persona VALUES('" + fhp.getIdpersona() + "','" + fhp.getIdfilm() + "');\r\n";

			// agginge la insert al vettore delle query per la tabella Persona.
			query.append(insertFHP);
			// scrittura della insert nel file script.
		}
		// scrive il file "scriptFilmHasPersona.sql"
		Utility.writeUtf8(query.toString(), Utility.getScriptPath(mode)+"/scriptFilmHasPersona.sql", false);
	}

	// metodo che crea lo script sql che popola la tabella sala del db.
	public void makeScriptSala(String mode) {

		StringBuffer query = new StringBuffer();
		Iterator<Sala> iter = map_sale.values().iterator();

		String insertSala = null; 

		// costruzione della query per ogni elemento del vettore.
		if(mode.equals("update")){
			query.append("DELETE FROM dbcinema.sala_proietta_film;\r\n");
			query.append("DELETE FROM dbcinema.sala;\r\n");
		}
		
		while (iter.hasNext()) {
			Sala s = (Sala) iter.next();
			// costruzione della query sql di INSERT INTO Sala.
			insertSala = "INSERT INTO dbcinema.sala VALUES('"+s.getCinema_idcinema()+"','"+s.getIdsala()+"','"+s.getNome()+"','"+s.getNum_posti()+"');\r\n";

			Pattern re = Pattern.compile("''");
			Matcher m = re.matcher(insertSala);
			insertSala = m.replaceAll("null");

			re = Pattern.compile("'null'");
			m = re.matcher(insertSala);
			insertSala = m.replaceAll("null");
			
			// agginge la insert al vettore delle query per la tabella Persona.
			query.append(insertSala);
		}
		Utility.writeUtf8(query.toString(), Utility.getScriptPath(mode)+"/scriptSala.sql", false);
	}

	// metodo che crea lo script sql che popola la tabella film_has_persona del db.
	public void makeScriptSala_Proietta_Film(String mode) {
		
		StringBuffer query = new StringBuffer();
		// lettura dei campi dalla hasmap e creazione dello script di insert.
		Iterator<SalaProiettaFilm> iter = salaProiettaFilm.iterator();

		String insertSPF = null; 
		// costruzione della query per ogni elemento del vettore.

		while (iter.hasNext()) {
			SalaProiettaFilm spf = (SalaProiettaFilm)iter.next();
			// costruzione della query sql di INSERT INTO Film Has Persona.
			insertSPF = "INSERT INTO dbcinema.sala_proietta_film VALUES('" + spf.getSala_cinema_idcinema()+ "','" + spf.getSala_idsala() +"','" + spf.getFilm_idfilm()+"','" + spf.getIdproiezione()+"','" + spf.getOra()+"','" + spf.isTre_d()+"','" + spf.getPrezzo()+"');\r\n";

			Pattern re = Pattern.compile("''");
			Matcher m = re.matcher(insertSPF);
			insertSPF = m.replaceAll("null");

			re = Pattern.compile("'null'");
			m = re.matcher(insertSPF);
			insertSPF = m.replaceAll("null");
		
			re = Pattern.compile("'false'");
			m = re.matcher(insertSPF);
			insertSPF = m.replaceAll("false");

			re = Pattern.compile("'true'");
			m = re.matcher(insertSPF);
			insertSPF = m.replaceAll("true");

			query.append(insertSPF);
		}
		// scrive il file "scriptFilmHasPersona.sql"
		Utility.writeUtf8(query.toString(), Utility.getScriptPath(mode)+"/scriptSalaProiettaFilm.sql", false);
	}

	// metodo che carica la <table>: tabella contenente le programmazioni di nottolasera.it in una stringa,
	// crea un vettore di stringhe e in ogni riga ci mette la programmazione di un cinema.
	public void loadProiezioni() {
		String page = null;
		String cleanedPage = null;

		page = Utility.readPage("http://www.nottolasera.it/Films.ps?OP=ProgZN&ZoneKey=299", "iso-8859-1");
		// si ripulisce la pagina dai caratteri speciali.
		cleanedPage = Utility.cleanXML(page);
		cleanedPage = Utility.getSubstring(cleanedPage, "In Programmazione", "Cerca la Programmazione", false);
		// si scrive con codifica utf-8 la pagina ripulita in un file.
		Utility.writeUtf8(cleanedPage, pathLavoro+"/programmazioneUtf8.html", true);

		String html = "";
		try {
			html = Utility.fileToString(pathLavoro+"/programmazioneUtf8.html");
		} catch (FileNotFoundException e) {
			System.out.println("File non trovato.");
			e.printStackTrace();
		}
		String proiezione = "";
		// inserisce nel vettore tutto ciò che è compreso nel tag <tr>
		while (html != null) {
			proiezione = Utility.getSubstring(html, "<tr vAlign=\"top\">", "</tr>", true);
			if (proiezione == null) {
				Utility.resetInedex();
				break;
			}
			proiezioni.add(proiezione);
		}
	}

	// metodo che recupera gli attributi delle sale.
	public void getProiezioneFields() {

		Sala s = null;
		String sala = null;
		Sala appSala = null;
		SalaProiettaFilm spf = null;
		Vector<String> sale = null;
		StringTokenizer st = null;
		Film f = null;
		
		// scorre il vettore proiezioni: ogni elemento = programmazione di un cinema.
		for (int i = 0; i < proiezioni.size(); i++) {

			int idSala = 0;
			String elemento = proiezioni.elementAt(i);
			String cinema = Utility.getSubstring(elemento, "<b>", "</b>", true);
			String orario_prezzo = null;

			sale = loadSale(elemento);

			// scorre il vettore sale: ogni elemento = film in una sala di un cinema.
			for (int j=0; j<sale.size();j++){
				sala = sale.elementAt(j);
				s = new Sala();
				String film = Utility.getSubstring(sala, "&ZoneKey=299\">", "</a>", true); // titolo film
				
				film = Utility.cleanFilmTitle(film).trim();
								
				s.nome = Utility.getSubstring(sala, "<td noWrap=\"noWrap\">", "<br />", true); // nome sala
				s.num_posti = Utility.getSubstring(sala, "Posti:", "</td>", true); // numero dei posti nella sala
				if(elenco_cinema.get(cinema)!=null)
					s.cinema_idcinema = elenco_cinema.get(cinema);
				else{
					System.out.println("CINEMA NON PRESENTE SU MYMOVIES : "+cinema);
					break;
				}
				
				// riempimento dell' hasmap sale senza duplicati
				appSala = map_sale.put(s.cinema_idcinema+s.nome, s);
				if (appSala == null)
					s.idsala = ++idSala;
				else
					s.idsala = appSala.idsala;

				String orari_prezzi = Utility.getSubstring(sala, "width=\"100\"", "></td>", true);
				Pattern re = Pattern.compile(",");
				Matcher m = re.matcher(orari_prezzi);
				orari_prezzi = m.replaceAll(".");

				Utility.resetInedex(); 
				int proiezione = 0;
				while (orari_prezzi != null) { // entra nel while solo se ci sono sale
					orario_prezzo = Utility.getSubstring(orari_prezzi, ">", "<br /", true);
					if (orario_prezzo == null)
						break;
					
					//riempimento hasmap sala_proietta_film
					spf = new SalaProiettaFilm();
					spf.sala_cinema_idcinema = s.cinema_idcinema;
					spf.sala_idsala = s.idsala;
					if ( (f=map_film.get(film.toLowerCase()))!=null){
						spf.film_idfilm= f.getIdfilm();
					}else{
						System.out.println("FILM NON PRESENTE in MYMOVIES : "+film);
						break;
					}					
					spf.idproiezione = ++proiezione;
					
					st = new StringTokenizer(orario_prezzo, "-");
					while(st.hasMoreElements()){
						spf.ora = st.nextToken().trim();
						spf.prezzo = st.nextToken().trim();
						spf.tre_d = false;
						if(spf.prezzo!=null & !spf.prezzo.isEmpty())
							if(new Float(spf.prezzo)>9)
								spf.tre_d = true;
					}
					salaProiettaFilm.add(spf);
					System.out.println("cinema : "+s.cinema_idcinema+" ,"+s.nome+",posti: "+s.num_posti+ " Ora : "+spf.ora+", film : "+film);
				}
				Utility.resetInedex(); 	
			}
			Utility.resetInedex(); 			
		}
		Utility.resetInedex(); 
	}

	public Vector<String> loadSale(String cinema) {
		Vector<String> sale = new Vector<String>();
		String sala = "";
		while (cinema != null) {
			sala = Utility.getSubstring(cinema, "<tr vAlign=\"Top\">", "<tr>", true);
			if (sala == null) {
				Utility.resetInedex(); 
				break;
			}
			sale.add(sala);
		}
		return sale;
	}

}// end class
